/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xos_http.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../msg_notify/head.h"
#include "../config/head.h"
#include "../macro/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "http_proc.h"

namespace xos_http
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    http_proc::http_proc()
    {
    }

    http_proc::~http_proc()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int http_proc::helper_download( response * pResponse )
    {
        int ret = 0;

        task * pTask = pResponse->m_pTask;
        file * pFile = pTask->m_pDownloadFile;
        int nSize = 0;

        if( 0 == ret )
        {
            const char * lpszFile = pFile->m_full_file.c_str();
            if( !pFile->m_full_gz_file.empty() )
            {
                pResponse->tags()->set( xos_http_protocol::HTTP_CONTENT_ENCODING, xos_http_protocol::HTTP_ZIP_DEFLATE );
                lpszFile = pFile->m_full_gz_file.c_str();
            }
            xos_box::i_list * pBodyList = pResponse->body_list();
            int nAccess = xos::i_file::XOS_FILE_READ;
            int nShare = xos::i_file::XOS_SHARE_READ;
            int nOpen = xos::i_file::XOS_FILE_OPEN;
            xos::i_file * pXosFile = mgr::container()->xos()->file();
            ret = pXosFile->open( lpszFile, nAccess, nShare, nOpen);
            if( 0 == ret )
            {
                nSize = pXosFile->read( pBodyList );
            }
            xos_stl::release_interface( pXosFile );
        }

        if( 0 == ret )
        {
            const char * pMime = 0;
            if( !pTask->m_ext.empty() )
            {
                pMime = mgr::container()->http()->get_mime_by_ext( pTask->m_ext.c_str() );
            }
            if( pMime )
            {
                pResponse->tags()->set( xos_http_protocol::HTTP_CONTENT_TYPE, pMime );
            }
        }

        if( 0 == ret )
        {
            ret = nSize;
        }

        return ret;
    }

    int http_proc::helper_404( response * pResponse )
    {
        int ret = 0;

        task * pTask = pResponse->m_pTask;
        request * pRequest = pTask->m_pRequest;

        xos_http_protocol::i_http_mgr * pMgr = mgr::container()->http();
        xos::i_crt * pCrt = mgr::container()->crt();

        xos_box::i_string * pInfo = mgr::container()->box()->string();
        xos_box::i_string * pHtml = mgr::container()->box()->string();

        xos_box::i_list * pBodyList = pResponse->body_list();
        xos_box::i_str_property * pTags = pResponse->tags();

        const char * lpszUri = pRequest->tags()->str( xos_http_protocol::HTTP_REQUEST_URI );

        pTags->set( xos_http_protocol::HTTP_REQUEST_RET_CODE, xos_http_protocol::HTTP_RET_CODE_404 );
        pCrt->sprintf( pInfo, "load file error : %s %s", lpszUri, xos_http_protocol::HTTP_RET_CODE_404);
        pMgr->fmt_simple_html( pHtml, lpszUri, pInfo->get(0, 0));

        {
            xos_box::i_buf * pBuf = mgr::container()->box()->buf();
            pBuf->add_data( pHtml->get( 0, 0 ), (int)pHtml->length(), true );
            pBodyList->push_back( pBuf );
            pBuf = 0;
        }

        xos_stl::release_interface( pInfo );
        xos_stl::release_interface( pHtml );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int http_proc::collect_filters( module::LIST & m_list, path * pPath )
    {
        int ret = 0;

        path * pTempPath = pPath;

        while( ( 0 == ret ) && pTempPath )
        {
            path * pFilter = (path *)pTempPath->get_path( config::get()->m_filter.c_str() );
            if( pFilter )
            {
                pFilter->get_filters( m_list );
            }
            pTempPath = pTempPath->m_pFatherPath;
        }

        return ret;
    }

    int http_proc::free_filters( module::LIST & m_list )
    {
        int ret = 0; 
        for( module::ITER iter = m_list.begin(); iter != m_list.end(); ++iter )
        {
            module * pM = *iter;
            xos_stl::release_ref( pM );
        }
        m_list.clear();
        return ret;
    }

    int http_proc::filter( task * pTask, const std::string & filter )
    {
        int ret = 0;

        module::LIST::reverse_iterator & riter = pTask->m_filter_list_riter;
        module::LIST::iterator & iter = pTask->m_filter_list_iter;
        bool bBefore = config::get()->m_before == filter;
        module::LIST & m_list = pTask->m_filter_list;

        while( 0 == ret )
        {
            module * pM = 0;
            if( bBefore )
            {
                if( iter != m_list.end() )
                {
                    pM = *iter++;
                }
            }
            else
            {
                if( riter != m_list.rend() )
                {
                    pM = *riter++;
                }
            }
            if( pM )
            {
                http::i_servlet * pS = pM->m_pServletMgr->create( filter.c_str() );
                ret = pS->proc( pTask );
                xos_stl::release_interface( pS );
            }
            else
            {
                break;
            }
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int http_proc::helper_quit_test( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)pMsg->get_void( 0, 0 );
        task::T * pTask = pConnect->m_pProcTask;
        const char * lpszTitle = "no uri path";
        const char * lpszInfo = 0;

        if( 0 == ret )
        {
            task::T * pTask = pConnect->m_pProcTask;
            xos_box::i_int_property * pPaths = pTask->m_pRequest->paths();
            int nIndexNum = 0;
            nIndexNum = pPaths->size();
            if( nIndexNum > 0 )
            {
                lpszTitle = pPaths->str( nIndexNum - 1 );
            }
            else
            {
                ret = 1;
            }
        }

        if( ( 0 == ret ) && ( 0 != mgr::container()->crt()->strcmp( lpszTitle, "quit" ) ) )
        {
            ret = 2;
        }

        if( 0 == ret )
        {
            xos_box::i_string * pStr = mgr::container()->box()->string();
            xos_box::i_buf * pBuf = mgr::container()->box()->buf();
            lpszInfo = pTask->m_pRequest->tags()->str( xos_http_protocol::HTTP_REQUEST_URI );
            mgr::container()->http()->fmt_simple_html( pStr, lpszTitle, lpszInfo );
            pBuf->add_data( pStr->get( 0, 0 ), (int)pStr->length(), true );
            pTask->http_response()->body_list()->push_back( pBuf );
            pBuf = 0;
            xos_stl::release_interface( pStr );
            xos_stl::release_interface( pBuf );
        }

        return ret;
    }

    int http_proc::proc( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        task::T * pTask = (task::T *)pMsg->get_void( 0, 0 );
        request * pRequest = pTask->m_pRequest;
        xos_box::i_int_property * pPaths = pRequest->paths();
        xos_box::i_str_property * pTags = pRequest->tags();
        xos_box::i_string * pStr = mgr::container()->box()->string();
        int nPathNum = 0;

        // 先准备好最终的uri列表(只有未指定uri时才会有多个配置的默认文件，正常只有一个请求的uri)
        {
            const char * lpszUri = pTags->str( xos_http_protocol::HTTP_REQUEST_URI );
            nPathNum = pPaths->size();
            if( 0 == nPathNum )
            {
                for( std::string & str : config::get()->m_welcome_file_list )
                {
                    pStr->set( lpszUri );
                    pStr->url_append( str.c_str() );
                    pTask->m_uri_list.push_back( pStr->get(0,0) );
                }
            }
            else
            {
                pTask->m_uri_list.push_back( lpszUri );
            }
            pTask->reset_uri_iters();
        }

        // 找到离目标uri层次最近的文件夹，用来构建filter链
        // 如果是xos文件，也可以用来查找载入的servlet
        {
            path * pPath = pTask->m_pRoot;
            for( int i = 0; i < nPathNum; ++i )
            {
                const char * lpszPath = pPaths->str( i );
                path * pT = (path *)pPath->get_path( lpszPath );
                if( pT )
                {
                    pPath = pT;
                }
                else
                {
                    break;
                }
            }
            // 构建filter链
            pTask->m_pPath = pPath;
            collect_filters( pTask->m_filter_list, pPath );
            pTask->reset_filter_iters();
        }

        // 
        // 准备参数。以后如果使用pTask->call之前，也要再次准备参数
        // 
        {
            const char * lpszServerParamName = config::get()->m_servlet.c_str();
            const char * lpszParam = pRequest->params()->str( lpszServerParamName );
            if( !lpszParam )
            {
                lpszParam = "";
            }
            pTask->values()->set( lpszServerParamName, lpszParam );
        }
        // 根据状态机，处理请求。在处理过程中，会反复发送FAST_JOB消息，甚至换到慢线程中执行慢任务，直接处理完成
        // 注意：net::i_connect仍有计数保护
        {
            pTask->m_eState = task::STATE_FILTER_BEFORE;
            pTask->get_connect()->fast_notify()->notify( pMsg, FAST_JOB );
        }

        xos_stl::release_interface( pStr );

        return ret;
    }

} // xos_http
